//
//  BaseBlockViewController.m
//  Effective Objective-C 2.0
//
//  Created by 周成龙 on 2017/12/26.
//  Copyright © 2017年 周成龙. All rights reserved.
//

#import "BaseBlockViewController.h"

typedef void(^blockName)(int a);// 别名

@interface BaseBlockViewController ()

@end

@implementation BaseBlockViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1. 基础知识
    ^{
        NSLog(@"简单的块");
    };
    void (^ someBlock)() = ^{
        NSLog(@"没有参数，没有返回值");
    };
    someBlock();
    //
    int (^additional) (int a, int b) = ^ (int a, int b) {
        NSLog(@"有参数，有返回");
        return a+b;
    };
    additional(10, 20);
    
    int addition = 5;
    int (^addBlock) (int a, int b) = ^(int a, int b) {
        NSLog(@"有参数，有返回，引用外部变量");
        return a+b+addition;
    };
    addBlock(10, 20);
    
    // 默认情况下，为块所捕获的变量，是不可以在块里面修改的，需要在前面添加 __block 修饰符
    NSArray <NSNumber *>*array = @[@0, @1, @2, @3, @4, @5, @6];
    __block NSInteger count = 0;
    [array enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj compare:@2] == NSOrderedAscending) {
            count ++;
        }
    }];
    NSLog(@"%zd", count);// count = 2;
    // 2. 内部结构
    /**
     块：
     void*                  :isa
     int                    :flags
     int                    :reserved
     void(*)(void*, ...)    :invoke
     struct*                :descriptor
     
     // 块描述符：
     unsigned long int      :reserved
     unsigned long int      :size
     void(*)(void*, void*)  :copy
     void(*)(void*, void*)  :dispose
     */
    // 函数的原型至少要接受一个 void* 型参数，此参数代表块。
    // 块 拷贝的并不是对象本身，而是 指向这些对象的指针变量
    // invoke 函数为何需要把块对象作为参数传进来呢？
    // 因为：执行块时，要从内存中把这些捕获到的变量读出来。
    
    // 全局快、堆块、栈块
    /**
     定义块的时候，其所占的内存区域是分配在栈中的(块只在定义它的那个范围内有效)。
     */
    void (^block)();
    BOOL isOK = NO;
    if (isOK) {
        block = ^{
            NSLog(@"Block A");
        };
    }else {
        block = ^{
            NSLog(@"Block A");
        };
    }
    block();
    // 这样的代码是有问题的，两块内存都是分配在栈内存中的。这样的代码可以编译，但是运行起来时而正确，时而错误。若编译器未覆写待执行的块，则程序照常运行，若覆写，则程序崩溃。
    // 更改如下
    
    void (^blockCopy)();
    if (isOK) {
        blockCopy = [^{
            NSLog(@"Block A");
        } copy];
    }else {
        blockCopy = [^{
            NSLog(@"Block A");
        } copy];
    }
    blockCopy();
}



@end
